﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
public class OutlineSystem : MonoBehaviour
{
    private List<GameObject> gameObjects = new List<GameObject>();
    private Material effectMat = null;
    private Material renderMat = null;

    private CommandBuffer commandBuffer = null;
    private RenderTexture renderTex = null;
    private Renderer targetEBO;
    private int meshcount = 0;
    
    public Color outLineColor = Color.green;      //renderMat
    [Range(0, 10)]
    public int outLineSize = 1;
    [Range(0, 50)]
    public int BlurSize = 5;


    // Start is called before the first frame update
    void Start()
    {
        commandBuffer = new CommandBuffer();
        renderTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
        commandBuffer.SetRenderTarget(renderTex);
        commandBuffer.ClearRenderTarget(true, true, Color.black);

        //创建材质
        effectMat = new Material(Shader.Find("Outline/OutlintEffect"));
        renderMat= new Material(Shader.Find("Outline/SingleColor"));
    }
    public void AddOutline(GameObject gameObject)
    {
        if (gameObjects.IndexOf(gameObject) != -1)
            return;
        gameObjects.Add(gameObject);
        targetEBO = gameObject.GetComponent<Renderer>();
        if (targetEBO is MeshRenderer)
        {
            var f = targetEBO.GetComponent<MeshFilter>();
            if (f != null && f.sharedMesh != null)
                meshcount = f.sharedMesh.subMeshCount;
        }
        for (int i = 0; i < meshcount; i++)
        {
            commandBuffer.DrawRenderer(targetEBO, renderMat, i, 0);
        }
    }

    public bool IsOutline(GameObject gameObject)
    {
        if (gameObjects.IndexOf(gameObject) != -1)
            return true;
        else
            return false;
    }

    public void CancelOutline(GameObject gameObject) {
        this.gameObjects.Remove(gameObject);
        Outline();
    }
    /// <summary>
    /// 取消所有的描边效果
    /// </summary>
    public void CancelOutlineAll() {
        commandBuffer.Clear();
        commandBuffer.SetRenderTarget(renderTex);
        commandBuffer.ClearRenderTarget(true, true, Color.black);
        this.gameObjects.Clear();
    }
    /// <summary>
    /// 重新计算描边命令缓冲区
    /// </summary>
    private void Outline()
    {
        commandBuffer.Clear();
        commandBuffer.SetRenderTarget(renderTex);
        commandBuffer.ClearRenderTarget(true, true, Color.black);

        foreach (GameObject gobj in gameObjects)
        {
            targetEBO = gobj.GetComponent<Renderer>();
            if (targetEBO is MeshRenderer)
            {
                var f = targetEBO.GetComponent<MeshFilter>();
                if (f != null && f.sharedMesh != null)
                    meshcount = f.sharedMesh.subMeshCount;
            }
            for (int i = 0; i < meshcount; i++)
            {
                commandBuffer.DrawRenderer(targetEBO, renderMat, i, 0);
            }
        }
    }


    private void OnEnable()
    {
        if (renderTex)
        {
            RenderTexture.ReleaseTemporary(renderTex);
            renderTex = null;
        }

        if (commandBuffer != null)
        {
            commandBuffer.Release();
            commandBuffer = null;
        }

    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (targetEBO == null) {
            Graphics.Blit(src, dest);
            return;
        }
        if (!targetEBO.gameObject.activeSelf) {
            Graphics.Blit(src, dest);
            return;
        }
        if (renderMat && renderTex && commandBuffer != null )
        {
            //用白色绘制出图像
            renderMat.SetColor("_outLineColor", Color.white);
            Graphics.ExecuteCommandBuffer(commandBuffer);
            //声明用来模糊的RT
            RenderTexture temp1 = RenderTexture.GetTemporary(src.width, src.width, 0);
            RenderTexture temp2 = RenderTexture.GetTemporary(src.width, src.width, 0);
            //先进行一次模糊，因为无法直接用循环叠加commandBuffer
            Graphics.Blit(renderTex, temp1, effectMat, 0);
            Graphics.Blit(temp1, temp2, effectMat, 1);
            //边缘实体强化 
            Graphics.Blit(temp2, temp1, effectMat, 4);
            Graphics.Blit(temp1, temp2, effectMat, 4);
            

            //执行多次边缘扩展
            for (int i = 0; i < outLineSize; i++)
            {
                Graphics.Blit(temp2, temp1, effectMat, 0);
                Graphics.Blit(temp1, temp2, effectMat, 1);

                Graphics.Blit(temp2, temp1, effectMat, 4);
                Graphics.Blit(temp1, temp2, effectMat, 4);
            }
            //执行多次模糊
            for (int i = 0; i < BlurSize; i++)
            {
                Graphics.Blit(temp2, temp1, effectMat, 0);
                Graphics.Blit(temp1, temp2, effectMat, 1);
            }
            //将模糊后的图片减去commandBuffer中的实心剪影
            effectMat.SetTexture("_RenderTex", renderTex);
            Graphics.Blit(temp2, temp1, effectMat, 2);
            //后期处理，叠入渲染成果
            effectMat.SetTexture("_OutLineTex", temp1);
            effectMat.SetColor("_OutlineColor", outLineColor);
            Graphics.Blit(src, dest, effectMat, 3);
            //释放RT
            RenderTexture.ReleaseTemporary(temp1);
            RenderTexture.ReleaseTemporary(temp2);
            return;
        }
        
    }
}